package cn.sylinx.horm.dialect.sql;

import cn.sylinx.horm.dialect.fs.FS;
import cn.sylinx.horm.dialect.fs.PlainFS;
import cn.sylinx.horm.exception.HORMException;
import cn.sylinx.horm.util.Pair;
import cn.sylinx.horm.util.StrKit;
import cn.sylinx.horm.util.Tuple;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

public final class DefaultPlainFlusentSqlBuildHelper {

    public static Tuple buildSelectSQL(PlainFS fluentSql) {

        StringBuilder sql = new StringBuilder();
        if (StrKit.isNotBlank(fluentSql.getHint())) {
            sql.append(fluentSql.getHint()).append(" ");
        }

        sql.append("SELECT");
        if (fluentSql.isDistinct()) {
            sql.append(" DISTINCT");
        }

        boolean hasSelected = false;
        if (StrKit.isNotBlank(fluentSql.getSelectColumns())) {
            sql.append(' ').append(fluentSql.getSelectColumns()).append(' ');
            hasSelected = true;
        }

        if (StrKit.isNotBlank(fluentSql.getSelectExpressionColumns())) {
            // 如果表达式不为空
            sql.append(hasSelected ? "," : "").append(" ").append(fluentSql.getSelectExpressionColumns()).append(" ");

        } else if (!hasSelected) {
            sql.append(" * ");
        }

        sql.append("FROM ").append(fluentSql.getTableName());
        if (fluentSql.getConditionSQL().length() > 0) {
            sql.append(" WHERE ").append(fluentSql.getConditionSQL().substring(FS.AND_STR.length()));
        }

        String groupBy = fluentSql.getGroupBySQL().toString();
        if (StrKit.isNotBlank(groupBy)) {
            sql.append(" GROUP BY").append(groupBy);
        }

        String orderBy = fluentSql.getOrderBySQL().toString();
        if (StrKit.isNotBlank(orderBy)) {
            sql.append(" ORDER BY").append(orderBy);
        }

        String limitSql = fluentSql.getLimitSQL().toString();
        if (StrKit.isNotBlank(limitSql)) {
            sql.append(limitSql);
        }

        int len = fluentSql.getParamValues() == null || fluentSql.getParamValues().isEmpty() ? 0
                : fluentSql.getParamValues().size();
        Object[] params = null;

        if (len > 0) {
            params = new Object[len];
            fluentSql.getParamValues().toArray(params);
        }

        return Tuple.apply(sql.toString(), params);
    }

    public static Pair buildUpdateSQL(PlainFS fluentSql) {

        List<Object> paramsUpdate = new ArrayList<Object>();

        StringBuilder sql = new StringBuilder();
        if (StrKit.isNotBlank(fluentSql.getHint())) {
            sql.append(fluentSql.getHint()).append(" ");
        }

        sql.append("UPDATE ").append(fluentSql.getTableName()).append(" SET ");

        StringBuilder setSQL = new StringBuilder();

        boolean gmtUpdated = false;

        if (null != fluentSql.getUpdateColumnExpression() && !fluentSql.getUpdateColumnExpression().isEmpty()) {
            // native sql expression
            fluentSql.getUpdateColumnExpression()
                    .forEach((key, value) -> setSQL.append(key).append(" = ").append(value).append(", "));
        }

        if (null == fluentSql.getUpdateColumns() || fluentSql.getUpdateColumns().isEmpty()) {
            throw new HORMException("update columns is empty");
        }

        fluentSql.getUpdateColumns().forEach((key, value) -> {
            if (value == null) {
                // 值为空情况
                setSQL.append(key).append(" = NULL, ");
            } else {
                setSQL.append(key).append(" = ?, ");
                paramsUpdate.add(value);
            }
        });

        sql.append(setSQL.substring(0, setSQL.length() - 2));
        if (fluentSql.getConditionSQL().length() > 0) {
            sql.append(" WHERE ").append(fluentSql.getConditionSQL().substring(FS.AND_STR.length()));
        }

        List<Object> paramsListFinal = new ArrayList<>();
        List<Object> paramsListPre = fluentSql.getParamValues();
        if (paramsListPre == null) {
            paramsListPre = new ArrayList<>();
        }
        paramsListFinal.addAll(paramsUpdate);
        paramsListFinal.addAll(paramsListPre);

        int len = paramsListFinal.size();

        Object[] params = null;

        if (len > 0) {
            params = new Object[len];
            paramsListFinal.toArray(params);
        }

        return Pair.of(sql.toString(), params);
    }

    public static Pair buildInsertSQL(PlainFS fluentSql) {

        StringBuilder columnNames = new StringBuilder();
        StringBuilder placeholder = new StringBuilder();
        List<Object> insertParams = new ArrayList<Object>();

        StringBuilder sql = new StringBuilder();
        if (StrKit.isNotBlank(fluentSql.getHint())) {
            sql.append(fluentSql.getHint()).append(" ");
        }

        sql.append("INSERT INTO ").append(fluentSql.getTableName());
        boolean hasField = false;

        if (null == fluentSql.getUpdateColumns() || fluentSql.getUpdateColumns().isEmpty()) {
            throw new HORMException("update columns is empty");
        }

        Map<String, Object> uc = fluentSql.getUpdateColumns();
        Set<Map.Entry<String, Object>> entrySets = uc.entrySet();
        for (Map.Entry<String, Object> entry : entrySets) {
            columnNames.append(",").append(entry.getKey());
            placeholder.append(",?");
            insertParams.add(entry.getValue());
            hasField = true;
        }

        if (!hasField) {
            throw new HORMException("no insert field detected");
        }

        sql.append("(").append(columnNames.substring(1)).append(")").append(" VALUES (")
                .append(placeholder.substring(1)).append(")");

        List<Object> paramsListFinal = new ArrayList<>(insertParams);
        int len = paramsListFinal.size();
        Object[] params = null;

        if (len > 0) {
            params = new Object[len];
            paramsListFinal.toArray(params);
        }

        return Pair.of(sql.toString(), params);
    }

    public static Pair buildDeleteSQL(PlainFS fluentSql) {

        List<Object> paramsDelete = new ArrayList<Object>();

        StringBuilder sql = new StringBuilder();
        if (StrKit.isNotBlank(fluentSql.getHint())) {
            sql.append(fluentSql.getHint()).append(" ");
        }

        sql.append("DELETE FROM ").append(fluentSql.getTableName());

        if (fluentSql.getConditionSQL().length() > 0) {
            sql.append(" WHERE ").append(fluentSql.getConditionSQL().substring(FS.AND_STR.length()));
            List<Object> paramsListPre = fluentSql.getParamValues();
            if (paramsListPre == null) {
                paramsListPre = new ArrayList<>();
            }
            paramsDelete.addAll(paramsListPre);
        }

        List<Object> paramsListFinal = new ArrayList<>(paramsDelete);
        int len = paramsListFinal.size();

        Object[] params = null;

        if (len > 0) {
            params = new Object[len];
            paramsListFinal.toArray(params);
        }

        return Pair.of(sql.toString(), params);
    }

    public static Pair buildCountSQL(PlainFS fluentSql) {

        if (!fluentSql.isCount()) {
            throw new HORMException("not count sql");
        }

        StringBuilder sql = new StringBuilder();
        if (StrKit.isNotBlank(fluentSql.getHint())) {
            sql.append(fluentSql.getHint()).append(" ");
        }

        sql.append("SELECT COUNT(*) ");

        sql.append("FROM ").append(fluentSql.getTableName());
        if (fluentSql.getConditionSQL().length() > 0) {
            sql.append(" WHERE ").append(fluentSql.getConditionSQL().substring(FS.AND_STR.length()));
        }

        int len = fluentSql.getParamValues() == null || fluentSql.getParamValues().isEmpty() ? 0
                : fluentSql.getParamValues().size();
        Object[] params = null;

        if (len > 0) {
            params = new Object[len];
            fluentSql.getParamValues().toArray(params);
        }

        return Pair.apply(sql.toString(), params);
    }

}
